<?php

namespace app\service;

use app\model\User;
use Firebase\JWT\JWK;
use Firebase\JWT\JWT;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Support\Str;
use polymerize\order\service\PayModelService;
use support\Log;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\Model;
use yanlongli\AppStoreServerApi\AppStoreServerApi;
use yanlongli\AppStoreServerApi\Environment;

class AppleService
{
    public static ?AppleService $_instance = null;
    const AUTH_KEYS_URL = 'https://appleid.apple.com/auth/keys'; //获取Apple公钥访问地址

    /**
     * @return AppleService|mixed
     */
    public static function instance(): ?AppleService
    {
        if (!static::$_instance) static::$_instance = new self();
        return static::$_instance;
    }

    /**
     * @param string $identityToken
     * @param string $ref
     * @param int    $bindUid
     * @return User|array|false|mixed|Model|null
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public function appleJwtVerify(string $identityToken = '', string $ref = '', int $bindUid = 0)
    {
        if (!$identityToken) return ['code' => 400, 'msg' => 'token不存在'];
        try {
            $public_key = file_get_contents(self::AUTH_KEYS_URL);
        } catch (\Exception $exception) {
            return ['code' => 400, 'msg' => '未知错误'];
        }
        if (empty($public_key)) return ['code' => 400, 'msg' => '公key不存在'];
        $keys = json_decode($public_key, true) ?? [];
        if (empty($keys)) return ['code' => 400, 'msg' => 'keys不存在'];
        list($head_db_64, ,) = explode('.', $identityToken);
        $head_db_res = JWT::jsonDecode(JWT::urlsafeB64Decode($head_db_64));

        $alg = $keys['keys'][0]['alg'];
        $kid = $keys['keys'][0]['kid'];
        foreach ($keys['keys'] as $value) {
            if ($value['kid'] == $head_db_res->kid) {
                $alg = $value['alg'];
                $kid = $value['kid'];
            }
        }
        //获取公钥
        $pem = JWK::parseKeySet($keys);
        //返回包含**详情的数组
        $publicKey = openssl_pkey_get_details($pem[$kid]);
        try {
            $decode = JWT::decode($identityToken, $publicKey['key'], [$alg]);
            try {
                $userInfo = (array)$decode;
                $unionid  = $userInfo['sub'] ?? '';
                if (!$unionid) return ['code' => 400, 'msg' => 'unionid不存在'];
                if ($bindUid) {
                    //绑定
                    $where[] = ['status', '=', 1];
                    $where[] = ['apple_sub', '=', $unionid];
                    $where[] = ['id', '<>', $bindUid];
                    $user    = User::where($where)->find();
                    if ($user) return ['code' => 400, 'msg' => '该Apple已被其他账号绑定'];

                    $where1[] = ['id', '=', $bindUid];
                    $user     = User::where($where1)->find();
                    if (!$user) return ['code' => 400, 'msg' => '绑定用户不存在'];

                    $data = [
                        'apple_sub' => $unionid,
                        'u_time'    => date('Y-m-d H:i:s'),
                    ];
                    User::updateDataCache($where1, $data);
                } else {
                    $where2[] = ['apple_sub', '=', $unionid];
                    $user     = User::where($where2)->find();
                }
                if (empty($user) && !$bindUid) {
                    $userInfo['nickname'] = 'apple-' . Str::random(5);
                    $user                 = User::apple_register($userInfo, $ref);
                }
                return ['code' => 200, 'data' => $user];
            } catch (\InvalidArgumentException $exception) {
                return ['code' => 400, 'msg' => '未知错误1'];
            }
        } catch (\UnexpectedValueException $exception) {
            return ['code' => 400, 'msg' => '未知错误2'];
        }
    }

    /**
     * 验证票据苹果支付是否成功，苹果客户端支付成功后调用
     * 客户端失败后得再调用一次
     * 传$receiptData付款后凭证票据信息
     * 原始订单号
     * 苹果订单号
     *
     * //in_app与latest_receipt_info
     * //测试时发现，这两个字段的数值几乎相同，不过有几点需要注意：
     * //（1）自动续订订阅类型，在到期后会再生成一条购买记录，这条记录会出现在last_receipt_info里，但不会出现在in_app里
     * //（2）自动续订订阅类型可以配置试用，试用记录只有在latest_receipt_info里，is_trial_period字段才是true
     * //（3）消耗型购买记录有可能不会出现在latest_receipt_info，因此需要检查in_app来确保校验正确
     *
     * @param string $receiptData 付款后凭证票据信息
     * @return array                验证是否成功
     */
    public function validateApplePay(string $receiptData, int $sandbox = 0): array
    {
        // 验证参数
        if (strlen($receiptData) < 20) return ['code' => 400, 'msg' => '非法参数'];

        // 请求验证
        $data = $this->acurl($receiptData, $sandbox);
        if ($data['code'] == 400) return $data;
        if (!isset($data['data']['status'])) return ['code' => 400, 'msg' => '购买失败'];
        $data = $data['data'];

        if (isset($_GET['debug'])) return ['code' => 200, 'data' => json_encode($data)];

        // 判断是否购买成功
        if (intval($data['status']) === 0) {
            return ['code' => 200, 'data' => $data];
        }

        return ['code' => 400, 'msg' => '购买失败 status:' . $data['status']];
    }

    /**
     * 验证AppStore内付
     * https://blog.csdn.net/qq_24909089/article/details/1032
     * 21000 App Store不能读取你提供的JSON对象
     * 21002 receipt-data域的数据有问题 该receipt-data属性中的数据格式错误或丢失。
     * 21003 receipt无法通过验证 收据无法认证。
     * 21004 提供的shared secret不匹配你账号中的shared secret 您提供的共享密码与您帐户的文件共享密码不匹配。
     * 21005 receipt服务器当前不可用
     * 21006 该收据有效，但订阅已过期。当此状态代码返回到您的服务器时，收据数据也会被解码并作为响应的一部分返回。仅针对自动续订的iOS 6样式交易收据返回。
     * 21007 该收据来自测试环境，但已发送到生产环境以进行验证。而是将其发送到测试环境
     * 21008 receipt是生产receipt，但却发送至Sandbox环境的验证服务。该收据来自生产环境，但是已发送到测试环境以进行验证。而是将其发送到生产环境。
     * 21010 此收据无法授权。就像从未进行过购买一样对待。
     * 21100-21199 内部数据访问错误。
     * 在测试环境中，https://sandbox.itunes.apple.com/verifyReceipt用作URL。
     * 在生产中，https://buy.itunes.apple.com/verifyReceipt用作URL。
     *
     * 在latest_receipt_info数组里，遍历数据，找到最大的expires_date，这个是订阅过期的日期，也可用expires_date_ms或expires_date_pst字段。服务器应保存该字段到数据库
     * product_id 是商品id，要与购买的商品ID相符才有效。
     * web_order_line_item_id 是本次购买的ID
     * original_transaction_id 是第一次订阅的交易ID，服务器应保存该字段到数据库
     */
    public function acurl($receiptData, $sandbox = 0)
    {
        //由苹果客户端开发者去苹果平台创建生成
        $password = 'owenweb';
        //小票信息
        $POSTFIELDS = array("receipt-data" => $receiptData, "password" => $password);

        //正式购买地址
        $url_buy = "https://buy.itunes.apple.com/verifyReceipt";
        //沙盒购买地址
        $url_sandbox = "https://sandbox.itunes.apple.com/verifyReceipt";
        $url         = $sandbox ? $url_sandbox : $url_buy;

        return curlPost($url, $POSTFIELDS);
    }

    //苹果历史订单查询，根据对应参数
    public static function appleServerApiHistory($param = []): bool
    {
        $time = time();
        $data = self::_query($param['original_transaction_id']);
        if (empty($data)) return false;
        $cnt = 0;
        if (isset($data->signedTransactions) && $data->signedTransactions) {
            foreach ($data->signedTransactions as $value) {
                if (isset($value->payload) && $value->payload) {
                    if (isset($value->payload->expiresDate)) {
                        $expiresDate = $value->payload->expiresDate / 1000;
                        if ($expiresDate < $time) {
                            //过期订单不处理
                            continue;
                        }
                    }
                    if (isset($value->payload->revocationDate)) {
                        $revocationDate = $value->payload->revocationDate / 1000;
                        //撤销订单即退款订单 暂不处理
                        continue;
                    }
                    //校验是否发放
                    //校验订单是否已经发放过  已发放不处理

                    //如果订单没有入库，则进行发放就行
                    $purchaseDate = $value->payload->purchaseDate / 1000;

//                    dump($value->payload->transactionId);
//                    dump($value->payload->originalTransactionId);
//                    dump($value->payload->productId);
//                    dump($value->payload->purchaseDate);
                }
            }
            $cnt = 1;
        }

        return $cnt ? true : false;
    }

    //苹果历史订单查询
    //https://blog.csdn.net/iOSTeam37/article/details/122683363
    public static function _query($originalTransactionId = '', int $sandbox = 0, string $package = 'com.tayun.FigureCharGing')
    {
        if (empty($originalTransactionId)) return false;

        //配置需要苹果客户端开发者去苹果平台创建，不需要改
        $privateKeyId = "owenweb";
        $privateKey   = '-----BEGIN PRIVATE KEY-----
owenweb
-----END PRIVATE KEY-----';
        $issuerId     = 'owenweb';

        $endpoint  = $sandbox ? Environment::ENDPOINT_SANDBOX : Environment::ENDPOINT_PRODUCTION;
        $serverApi = new AppStoreServerApi(
            $endpoint,
            $privateKeyId,
            $privateKey,
            $issuerId,
            $package,// Optional,可选的，如果仅管理单个应用可以设定默认值。
        );

        //这里是通过原始订单号来查
        try {
            return $serverApi->history($originalTransactionId);
        } catch (GuzzleException $e) {
            return $e->getMessage();
        }
        //这里是通过用户账单的订单号来查
//        $data = $serverApi->lookup($originalTransactionId);
    }

    //连续订阅才会走苹果回调处理
    public function appleNotify($signedPayload): array
    {
        if (empty($signedPayload)) return ['code' => 400, 'msg' => 'empty signedPayload'];

        $jws = \JOSE_JWT::decode($signedPayload);
        if (isset($jws->claims)) {
            $notifyData       = $jws->claims['data'];
            $notificationType = $jws->claims['notificationType'];

            if ($notifyData) {
                $signedTransactionInfo = \JOSE_JWT::decode($notifyData->signedTransactionInfo);

                if (isset($signedTransactionInfo->claims)) {
                    $transactionInfo       = $signedTransactionInfo->claims;
                    $transactionId         = $transactionInfo['transactionId'];
                    $originalTransactionId = $transactionInfo['originalTransactionId'];
                    $payTime               = $transactionInfo['purchaseDate'];
                    $payTimeByBeiJing      = millisecondTimeToformat($payTime);

                    //根据回调类型处理
                    switch ($notificationType) {
                        //自动续费
                        case "DID_RENEW"                :
                            return PayModelService::instance()->addModelByApplePayDidRenew($payTimeByBeiJing, $transactionId, $originalTransactionId);
                        //过期
                        case "EXPIRED"                   :
                            (Log::channel('applepaylog'))->info('EXPIRED', (array)$jws->claims);
                            return ['code' => 400, 'msg' => 'EXPIRED'];
                        //退款
                        case "REFUND"                    :
                            return PayModelService::instance()->updateModelByRefund($transactionId, $originalTransactionId, 'appleNotify回调退款', 3);
                        //订阅（订阅成功后都会发）
                        //获取不到用户信息，就不处理了,客户端会调用订单支付查询接口
                        case "SUBSCRIBED"                :
                            return ['code' => 400, 'msg' => 'SUBSCRIBED'];
                        //变更订阅状态（订阅取消、订阅升档、订阅降档）
                        case "DID_CHANGE_RENEWAL_STATUS" :
                            (Log::channel('applepaylog'))->info('DID_CHANGE_RENEWAL_STATUS', (array)$jws->claims);
                            return ['code' => 400, 'msg' => 'DID_CHANGE_RENEWAL_STATUS'];
                        default                     :
                            (Log::channel('applepaylog'))->info('empty notificationType', (array)$jws->claims);
                            return ['code' => 400, 'msg' => 'empty notificationType'];
                    }
                }
            }
        }

        return ['code' => 400, 'msg' => 'empty jws_claims'];
    }


}
